<?php
/**
 ***********************************************************************************************************************
 * Active record
 * @author llq
 ***********************************************************************************************************************
 */
namespace artisan\db;
/**
 * Class MySQLiAR
 * @package artisan\db
 */
/**
 * Class MySQLiAR
 * @package artisan\db
 */
class MySQLiAR
{
    /**
     * @var null 当前实例连接别名
     */
    protected $alias = null;

    /**
     * @var bool|null 当前实例连接数据库主从标识
     */
    protected $flag = null;

    /**
     * @var bool 当前连接是否在进行事务操作
     */
    protected $inTransaction = false;

    /**
     * @var int 上次Query影响行数
     */
    protected $affectedRows = 0;

    /**
     * @var string 上次执行的SQL语句
     */
    protected $lastQuery = '';

    /**
     * @var float 上次执行SQL耗时
     */
    protected $lastQueryTime = 0.00;

    /**
     * @var string 当前操作使用的数据表
     */
    protected $currentTbl = '';

    /**
     * @var array 当前操作使用的Where条件
     */
    protected $currentWhere = [];

    /**
     * @var null 当前操作使用的Select字段，默认检索全部字段
     */
    protected $currentSelect = '*';

    /**
     * @var array 当前操作使用的排序条件
     */
    protected $currentOrderBy = [];

    /**
     * @var array 当前操作使用的分组条件
     */
    protected $currentGroupBy = [];

    /**
     * @var array 当前操作使用联合查询表们
     */
    protected $currentJoinTbls = [];

    /**
     * @var array 当前操作使用联合查询条件
     */
    protected $currentJoinOns = [];

    /**
     * @var array 当前操作使用联合查询类型
     */
    protected $currentJoinTypes = [];

    /**
     * @var string 当前操作使用的条数限制
     */
    protected $currentlimit = '';

    /**
     * @var array 当前类的所有实例列表
     */
    protected static $instances = [];

    /**
     * @var string 当前Query执行的错误信息
     */
    protected $currentError = '';

    /**
     * 创建或复用一个AR实例
     *
     * @param string $alias
     * @param null|bool $useMaster 是否使用主库 true强制使用主库 false强制使用从库 null智能选择
     * @return MySQLiAR
     */
    public static function connect($alias, $useMaster = null)
    {
        $key = md5((string)$alias);
        if(!isset(self::$instances[$key])) {
            self::$instances[$key] = new MySQLiAR($alias, $useMaster);
        }
        self::$instances[$key]->flag = $useMaster;
        return self::$instances[$key];
    }

    /**
     * 初始化一个AR实例
     *
     * @param string $alias
     * @param null|bool $flag
     */
    public function __construct($alias, $flag = null)
    {
        $this->alias = $alias;
        $this->flag = $flag;
    }

    /**
     * 设置当前数据表
     *
     * @param string $tbl
     * @return $this
     */
    public function table($tbl)
    {
        $this->currentTbl = trim((string)$tbl);
        return $this;
    }

    /**
     * 组织FROM条件
     *
     * @return string
     */
    protected function _from()
    {
        return 'FROM ' . $this->currentTbl . ' ';
    }

    /**
     * 设置SELECT条件
     *
     * @param string $select
     * @return $this
     */
    public function select($select = '*')
    {
        $select = is_array($select) ? implode(',', $select) : $select;
        $this->currentSelect = trim((string)$select);
        return $this;
    }

    /**
     * 组织SELECT条件
     *
     * @return string
     */
    protected function _select()
    {
        if(empty($this->currentSelect)) {
            return 'SELECT ' . $this->currentTbl . '.* ';
        }
        $arr = [];
        foreach((array)explode(',', $this->currentSelect) as $val) {
            $arr[] = (preg_match('/[.]/', $val) ? '' : ($this->currentTbl . '.')) . trim($val);
        }
        return 'SELECT ' . implode(',', $arr) . ' ';
    }

    /**
     * 设置分组条件
     *
     * @param string $groupBy
     * @return $this
     */
    public function groupBy($groupBy = '')
    {
        $this->currentGroupBy = trim((string)$groupBy);
        return $this;
    }

    /**
     * 组织分组条件
     *
     * @return string
     */
    protected function _groupBy()
    {
        if(empty($this->currentGroupBy)) {
            return ' ';
        }
        $arr = [];
        foreach((array)explode(',', $this->currentGroupBy) as $val) {
            $arr[] = (preg_match('/[.]/', $val) ? '' : ($this->currentTbl . '.')) . trim($val);
        }
        return 'GROUP BY ' . implode(',', $arr) . ' ';
    }

    /**
     * 设置排序条件
     *
     * @param string $orderBy
     * @return $this
     */
    public function orderBy($orderBy = '')
    {
        $this->currentOrderBy = trim((string)$orderBy);
        return $this;
    }

    /**
     * 组织排序条件
     *
     * @return string
     */
    protected function _orderBy()
    {
        if(empty($this->currentOrderBy)) {
            return ' ';
        }
        $arr = [];
        foreach((array)explode(',', $this->currentOrderBy) as $val) {
            $arr[] = (preg_match('/[.]/', $val) ? '' : ($this->currentTbl . '.')) . trim($val);
        }
        return 'ORDER BY ' . implode(',', $arr) . ' ';
    }

    /**
     * 设置结果集限制
     *
     * @param mixed $limit
     * @return $this
     */
    public function limit($limit = '')
    {
        $this->currentlimit = $limit;
        return $this;
    }

    /**
     * 组织结果集限制
     *
     * @return string
     */
    protected function _limit()
    {
        if(empty($this->currentlimit)) {
            return [];
        }

        // 支持传入[10,200]这种字符串格式
        $this->currentlimit = (is_string($this->currentlimit) && preg_match('/[,]/', $this->currentlimit))
            ?
                explode(',', $this->currentlimit)
            :
                $this->currentlimit;

        if(is_array($this->currentlimit) && count($this->currentlimit) == 2) {
            return [
                'limit' => 'LIMIT ?,? ',
                'params' => [
                    intval($this->currentlimit[0]),
                    intval($this->currentlimit[1])
                ]
            ];
        }

        return [
            'limit' => 'LIMIT ? ',
            'params' => [
                intval($this->currentlimit),
            ]
        ];
    }


    /**
     * 设置联表查询
     *
     * @param string $joinTbl
     * @param string $joinOn
     * @param string $joinType
     * @return $this
     */
    public function join($joinTbl = '', $joinOn = '', $joinType = 'LEFT JOIN')
    {
        if(
            empty($joinTbl) ||
            !in_array(
                strtoupper(trim((string)$joinType)),
                [
                    'LEFT JOIN',
                    'RIGHT JOIN',
                    'INNER JOIN',
                    'OUTER JOIN'
                ]
            )
        ) {
            return $this;
        }
        $this->currentJoinTbls[$joinTbl] = trim((string)$joinTbl);
        $this->currentJoinTypes[$joinTbl] = trim(strtoupper((string)$joinType));
        $this->currentJoinOns[$joinTbl] = trim((string)$joinOn);
        return $this;
    }

    /**
     * 组织联表查询
     *
     * @return string
     */
    protected function _join()
    {
        if(empty($this->currentJoinTbls)) {
            return '';
        }

        $join_str = ' ';
        foreach($this->currentJoinTbls as $tbl) {
            $join_str .= $this->currentJoinTypes[$tbl] . ' ' . $tbl . ' ON(' . $this->currentJoinOns[$tbl].') ';
        }
        return $join_str . ' ';
    }

    /**
     * 设置Where条件
     *
     * @param array $where
     * @return $this
     */
    public function where(array $where = [])
    {
        $this->currentWhere = $where;
        return $this;
    }

    /**
     * 组织Where条件
     *
     * @return array|bool
     */
    protected function _where()
    {
        if(empty($this->currentWhere)) {
            return [];
        }

        $condition = [];
        $params = [];
        $symbols = [
            '=',
            '>',
            '<',
            '>=',
            '<=',
            '!=',
            '<>',
            'LIKE',
            'NOT LIKE',
            'IN',
            'NOT IN',
        ];

        foreach((array)$this->currentWhere as $key => $val) {
            $key = trim(preg_replace('/\s+/', ' ', $key));
            $pos = strpos($key, ' ');
            $field = ($pos !== false) ? substr($key, 0, $pos) : $key;
            $symbol = strtoupper(($pos === false) ? '=' : trim(substr($key, $pos)));

            // 安全考虑 非法操作符依赖返回where条件解析失败
            if(!in_array($symbol, $symbols)) {
                return false;
            }

            // LIKE查询特殊处理
            if($symbol == 'LIKE' || $symbol == 'NOT LIKE') {
                $condition[] = "`" . $field . "` " . $symbol . " ?";
                // 自动两侧模糊匹配
                $params[] = (strpos($val, '%') === false) ? "%" . $val . "%" : $val;

                // IN查询特殊处理
            } else if(($symbol == 'IN' || $symbol == 'NOT IN')) {

                // 支持格式123,134 或 (123,134)
                if(is_string($val) || is_numeric($val)) {
                    $val = explode(',', preg_replace('/[\(]|[\)]/', '', $val));
                }

                // 其他类型一律强制转化成array
                $tmp = [];
                foreach((array)$val as $v) {
                    $tmp[] = '?';
                    $params[] = addslashes(trim((string)$v));
                }
                $condition[] = "`" . $field . "` " . $symbol . " (" . implode(',', $tmp) . ")";

                // 其他类型处理
            } else {
                // 其他场景val一律强制转换成string
                $val = (is_numeric($val) || is_float($val)) ? $val : (string)$val;
                // null值特殊处理
                $valTmp = strtoupper(trim(preg_replace('/\s+/', ' ', $val)));
                if($valTmp == 'IS NULL' || $valTmp == 'IS NOT NULL') {
                    $condition[] = "`" .$field . "` " . $valTmp;
                } else {
                    $condition[] = "`" . $field . "` " . $symbol . " ?";
                    $params[] = $val;
                }
            }

        }

        // 安全考虑 非法操作符依赖返回where条件解析失败
        if(empty($condition)) {
            return false;
        }

        return [
            'where' => 'WHERE ' . implode(' AND ', $condition) . ' ',
            'params' => $params
        ];
    }

    /**
     * 常规方式写入
     *
     * @param array $data 需要写入的值支持二维数组，批量写入多行记录
     * @return bool
     */
    public function insert(array $data)
    {
        return $this->buildInsert($data);
    }

    /**
     * 忽略方式写入 唯一键值存在忽略写入
     *
     * @param array $data 需要写入的值支持二维数组，批量写入多行记录
     * @return bool
     */
    public function insertWithIgnore(array $data)
    {
        return $this->buildInsert($data, 'ignore');
    }

    /**
     * 替换方式写入 唯一键值存在替换数据
     *
     * @param array $data 需要写入的值支持二维数组，批量写入多行记录
     * @return bool
     */
    public function insertWithReplace(array $data)
    {
        return $this->buildInsert($data, 'replace');
    }

    /**
     * 自定义方式写入 自定义需要更新的字段 一般批量更新数据可以用该方法来实现
     *
     * @param array $data 需要写入的值支持二维数组，批量写入多行记录
     * @param string $field2update 自定义ON DUPLICATE时要更新的字段  如ip=values(ip), api=values(api), cnt=cnt+1等等
     * @return bool
     */
    public function insertWithDuplicate(array $data, $field2update)
    {
        if(empty($field2update)) {
            return false;
        }
        return $this->buildInsert($data, 'duplicate', (string)$field2update);
    }

    /**
     * 格式化写入数据 支持多种类型写入
     *
     * @param array $data 需要写入的值支持二维数组，批量写入多行记录
     * @param string $type
     * @param string $field2update 如ip=values(ip), api=values(api), cnt=cnt+1等等
     * @return bool
     */
    protected function buildInsert(array $data, $type = 'insert', $field2update = '')
    {
        if(empty($this->currentTbl) || empty($data)) {
            return false;
        }

        $operates = [
            'insert' => 'INSERT INTO ',
            'ignore' => 'INSERT IGNORE INTO ',
            'replace' => 'REPLACE INTO ',
            'duplicate' => 'INSERT INTO ',
        ];
        $operate = $operates[$type];

        // 初始化需要的值
        $field_arr = [];
        $value_arr = [];
        $params = [];

        // 判断是否是批量写入
        $isbatch = is_array(end($insertData)) ? true : false;

        // 单条写入
        if($isbatch === false) {
            foreach($data as $field => $value) {
                $field_arr[] = "`" . $field . "`";
                $value_arr[] = '?';
                $params[] = $this->formatVal($value);
            }
            $field_str = implode(',', $field_arr);
            $value_str = implode(',', $value_arr);

            // 自定义ON DUPLICATE时要更新的字段
            $key_update_str = '';
            if($type == 'duplicate') {
                $key_update_str .= 'ON DUPLICATE KEY UPDATE ' . $field2update;
            }
            $sql = "INSERT INTO {$this->currentTbl} ({$field_str}) VALUES ({$value_str}) {$key_update_str}";
            return $this->bindQuery($sql, $params, true) ? true : false;
        }

        // 批量写入
        $field_tmp = [];
        foreach($data as $value) {
            $value_tmp = [];
            foreach($value as $field => $val) {
                if(!in_array($field, $field_tmp)) {
                    $field_tmp[] = $field;
                    $field_arr[] = "`" . $field . "`";
                }
                $value_tmp[] = '?';
                $params[] = $this->formatVal($val);
            }
            $value_arr[] = ' (' . implode(',', $value_tmp) . ')';
        }
        $field_str = implode(',', $field_arr);
        $value_str = implode(',', $value_arr);

        // 自定义ON DUPLICATE时要更新的字段
        $key_update_str = '';
        if($type == 'duplicate') {
            $key_update_str .= 'ON DUPLICATE KEY UPDATE ' . $field2update;
        }
        $sql = $operate . " {$this->currentTbl} ({$field_str}) VALUES {$value_str} {$key_update_str}";
        $this->reset();
        return $this->bindQuery($sql, $params, true) ? true : false;
    }

    /**
     * 更新数据
     *
     * @param array $where
     * @param array $data
     * @return bool
     */
    public function update(array $where = [], array $data)
    {
        // 输入检测
        if(empty($this->currentTbl) || empty($data) || (empty($this->currentWhere) && empty($where))) {
            return false;
        }
        // 拼接条件
        $where = $this->where($where ? $where : $this->currentWhere)->_where();
        // 条件解析失败
        if($where === false || empty($where['where'])) {
            $this->reset();
            return false;
        }
        $params = [];
        $vals = array();
        foreach($data as $key => $val) {
            $vals[] = $this->currentTbl . '.' . $key . "=?";
            $params[] = $this->formatVal($val);
        }
        $val_str = implode(',', $vals);
        $sql = "UPDATE {$this->currentTbl} SET {$val_str} {$where['where']}";
        $this->reset();
        return $this->bindQuery($sql, array_merge($params, $where['params']), true) ? true : false;
    }


    /**
     * 更新加1字段
     *
     * @param array $where
     * @param string $field
     * @return bool
     */
    public function incr(array $where = [], $field = '')
    {
        return $this->incrDecr($where, $field, '+');
    }

    /**
     * 更新减1字段
     *
     * @param array $where
     * @param string $field
     * @return bool
     */
    public function decr(array $where = [], $field = '')
    {
        return $this->incrDecr($where, $field, '-');
    }

    /**
     * 更新自定义加字段
     *
     * @param array $where
     * @param string $field
     * @param int $nu
     * @return bool
     */
    public function incrBy(array $where = [], $field = '', $nu = 1)
    {
        return $this->incrDecr($where, $field, '+', intval($nu));
    }

    /**
     * 更新自定义减字段
     *
     * @param array $where
     * @param string $field
     * @param int $nu
     * @return bool
     */
    public function decrBy(array $where = [], $field = '', $nu = 1)
    {
        return $this->incrDecr($where, $field, '-', intval($nu));
    }

    /**
     * @param array $where
     * @param string $field
     * @param string $operate
     * @param int $nu
     * @return bool
     */
    protected function incrDecr(array $where = [], $field = '', $operate = '+', $nu = 1)
    {
        if(empty($this->currentTbl) || empty($where) || empty($field) || !is_string($field)) {
            return false;
        }
        // 拼接条件
        $where = $this->where($where ? $where : $this->currentWhere)->_where();
        // 条件解析失败
        if($where === false || empty($where['where'])) {
            $this->reset();
            return false;
        }
        $val_str = $this->currentTbl . '.' . $field . "={$field} {$operate} $nu ";
        $sql = "UPDATE {$this->currentTbl} SET {$val_str} {$where['where']}";
        $this->reset();
        return $this->bindQuery($sql, $where['params'], true) ? true : false;
    }


    /**
     * 删除数据
     *
     * @param array $where
     * @return bool
     */
    public function delete(array $where = [])
    {
        return $this->del($where);
    }

    /**
     * 删除数据
     *
     * @param array $where
     * @return bool
     */
    public function del(array $where = [])
    {
        // 不传条件 直接当成危险查询处理
        if(empty($this->currentTbl) || (empty($where) && empty($this->currentWhere))) {
            return false;
        }
        // 拼接条件
        $where = $this->where($where ? $where : $this->currentWhere)->_where();
        // 条件解析失败
        if($where === false || empty($where['where'])) {
            $this->reset();
            return false;
        }
        $sql = "DELETE  FROM {$this->currentTbl} {$where['where']}";
        // 重置条件
        $this->reset();
        return $this->bindQuery($sql, $where['params'], true) ? true : false;
    }

    /**
     * 获取多条数据
     *
     * @param array $where
     * @param string $limit
     * @param string $orderBy
     * @return array|bool
     */
    public function getAll(array $where = [], $limit = '', $orderBy = '')
    {
        if($where) {
            $this->where($where);
        }
        if($limit) {
            $this->limit($limit);
        }
        if($orderBy) {
            $this->orderBy($orderBy);
        }
        return $this->get();
    }

    /**
     * 获取一条数据
     *
     * @param array $where
     * @param string $orderBy
     * @return array|bool
     */
    public function getOne(array $where = [], $orderBy = '')
    {
        if($where) {
            $this->where($where);
        }
        if($orderBy) {
            $this->orderBy($orderBy);
        }
        if(!empty($ret = $this->limit(1)->get()) && is_array($ret)) {
            return current($ret);
        }
        return [];
    }

    /**
     * @return array|bool
     */
    public function get()
    {
        $params = [];

        // 拼接sql
        $sql = $this->_select() . $this->_from() . $this->_join();

        // where条件组装
        $where_arr = $this->_where();
        // where条件解析失败 直接报错
        if($where_arr === false) {
            // 重置检索条件
            $this->reset();
            return false;
        }

        $sql .= !empty($where_arr) ? $where_arr['where'] : '';
        if(!empty($where_arr['params'])) {
            $params = $where_arr['params'];
        }

        $sql .= $this->_groupBy(). $this->_orderBy();

        // limit条件组装
        $limit_arr = $this->_limit();
        $sql .= !empty($limit_arr) ? $limit_arr['limit'] : '';
        if(!empty($limit_arr['params'])) {
            $params = array_merge($params, $limit_arr['params']);
        }

        // 重置检索条件
        $this->reset();

        // 执行Query
        return $this->bindQuery($sql, $params) ?: [];
    }

    /**
     * 统计条数
     *
     * @param array $where
     * @return int
     */
    public function count(array $where = [])
    {
        if(empty($this->currentTbl)) {
            return false;
        }
        $sql = "SELECT count(*) as cnt " . $this->_from() . $this->_join();
        // 拼接条件
        $where_arr = $this->where($where ? $where : $this->currentWhere)->_where();

        // 条件解析失败
        if($where_arr === false) {
            $this->reset();
            return 0;
        }
        if(!empty($where_arr['params']) && !empty($where_arr['where'])) {
            $sql .= $where_arr['where'] . $this->_groupBy() . $this->_orderBy();
            $ret = $this->bindQuery($sql, $where_arr['params']);
        } else {
            $sql .= $this->_groupBy() . $this->_orderBy();
            $ret = $this->query($sql);
        }
        $this->reset();
        return !empty($ret[0]['cnt']) ? $ret[0]['cnt'] : 0;
    }


    /**
     * @param $sql
     * @param array $bindParams
     * @param null $flag
     * @return bool|\mysqli_result
     */
    public function bindQuery($sql, array $bindParams = [], $flag = null)
    {
        // 重置影响条数
        $this->affectedRows = 0;
        // 重置错误信息
        $this->currentError = '';
        // 类型检测
        if(empty($sql) || !is_string($sql)) {
            $this->currentError = 'Empty or object sql give';
            return false;
        }
        // 尝试创建连接
        $this->reset();
        $connection = $this->getConnection($flag, $sql);
        // 成功创建连接
        if($connection) {
            // 尝试执行SQL
            $resource = $connection->bindQuery($sql, $bindParams);
            // 记录执行SQL
            $this->lastQuery = $connection->lastQuery();
            // 记录执行时间
            $this->lastQueryTime = $connection->lastQueryTime();
            // 记录影响行数
            $this->affectedRows = $connection->affectedRows();
            // 记录错误
            $this->currentError = $connection->getError();

            // 解析结果
            switch($this->parseMethod($sql)) {
                case 'INSERT':
                case 'REPLACE':
                    if(!$resource || $connection->affectedRows() <= 0) {
                        return false;
                    }
                    return true;
                    break;
                case 'UPDATE':
                case 'DELETE':
                case 'SELECT':
                case 'SHOW':
                case 'DESCRIBE':
                case 'EXPLAIN':
                    return $resource;
                    break;
                default:
                    return $resource;
                    break;
            }
        } else {
            $this->currentError = 'Get connection failed';
            return false;
        }
    }

    /**
     * @param $sql
     * @param null $flag
     * @return array|bool|\mysqli_result
     */
    public function query($sql, $flag = null)
    {
        // 重置影响条数
        $this->affectedRows = 0;
        // 重置错误信息
        $this->currentError = '';
        // 类型检测
        if(empty($sql) || !is_string($sql)) {
            $this->currentError = 'Empty or object sql give';
            return false;
        }
        // 尝试创建连接
        $this->reset();
        $connection = $this->getConnection($flag, $sql);
        // 成功创建连接
        if($connection) {
            // 尝试执行SQL
            $resource = $connection->query($sql);
            // 记录执行SQL
            $this->lastQuery = $connection->lastQuery();
            // 记录执行时间
            $this->lastQueryTime = $connection->lastQueryTime();
            // 记录影响行数
            $this->affectedRows = $connection->affectedRows();
            // 记录错误
            $this->currentError = $connection->getError();

            // 解析结果
            switch($this->parseMethod($sql)) {
                case 'INSERT':
                case 'REPLACE':
                    if(!$resource || $connection->affectedRows() <= 0) {
                        return false;
                    }
                    return true;
                    break;
                case 'UPDATE':
                case 'DELETE':
                    return $resource;
                    break;
                case 'SELECT':
                case 'SHOW':
                case 'DESCRIBE':
                case 'EXPLAIN':
                    return $connection->fetchToArray($resource);
                    break;
                default:
                    return $resource;
                    break;
            }
        } else {
            $this->currentError = 'Get connection failed';
            return false;
        }
    }


    /**
     * 解释所使用方法
     *
     * @param $sql
     * @return bool
     */
    protected function parseMethod($sql)
    {
        $sql = strtoupper(trim(preg_replace('/\s+/', ' ', (string)$sql)));
        if(preg_match('/^(SELECT|SHOW|DESCRIBE|EXPLAIN|INSERT|UPDATE|DELETE|REPLACE)/', $sql, $matche) && !empty($matche[1])) {
            return $matche[1];
        }
        return null;
    }

    /**
     * 重置查询条件
     */
    protected function reset()
    {
        $this->currentTbl = null;
        $this->currentSelect = null;
        $this->currentWhere = [];
        $this->currentOrderBy = [];
        $this->currentGroupBy = [];
        $this->currentJoinOns = [];
        $this->currentJoinTbls = [];
        $this->currentJoinTypes = [];
        $this->currentlimit = 0;
        $this->lastQuery = [];
        $this->lastQueryTime = 0.00;
        $this->affectedRows = 0;
    }

    /**
     * 获取上次执行查询语句
     *
     * @return string
     */
    public function lastQuery()
    {
        return $this->lastQuery;
    }

    /**
     * 获取上次执行查询语句
     *
     * @return string
     */
    public function getLastQuery()
    {
        return $this->lastQuery();
    }

    /**
     * 获取上次执行查询语句耗时
     *
     * @return float
     */
    public function lastQueryTime()
    {
        return $this->lastQueryTime;
    }


    /**
     * 获取上次执行查询影响条数
     *
     * @return int
     */
    public function affectedRows()
    {
        return $this->affectedRows;
    }

    /**
     * 获取上次执行查询影响条数
     *
     * @return int
     */
    public function getAffectedRows()
    {
        return $this->affectedRows;
    }

    /**
     * @return mixed
     */
    public function insertId()
    {
        $connection = $this->getConnection(true);
        if($connection) {
            return $connection->insertId();
        }
        return 0;
    }

    /**
     * 获取上次插入id
     */
    public function getInsertId()
    {
        return $this->insertId();
    }


    /**
     * 获取上次执行错误
     */
    public function lastError()
    {
        return $this->currentError;
    }

    /**
     * 事务操作 开始
     */
    public function startTransaction()
    {
        $this->inTransaction = true;
        $connection = $this->getConnection(true);
        if($connection) {
            return $connection->startTransaction();
        }
        return false;
    }

    /**
     * 事务操作 提交
     */
    public function commit()
    {
        $connection = $this->getConnection(true);
        if($connection) {
            return $connection->commit();
        }
        return false;
    }

    /**
     * 事务操作 回滚
     */
    public function rollback()
    {
        $connection = $this->getConnection(true);
        if($connection) {
            return $connection->rollback();
        }
        return false;
    }

    /**
     * 遍历结果集
     *
     * @param $resource
     * @return array
     */
    public function fetchToArray($resource)
    {
        // 兼容旧版的方法
        if(is_array($resource)) {
            return $resource;
        }

        if(empty($resource)) {
            return [];
        }

        $result = [];
        while($row = mysqli_fetch_assoc($resource)) {
            $result[] = $row;
        }
        return $result;
    }

    /**
     * 格式化值
     *
     * @param $val
     * @return string
     */
    protected function formatVal($val)
    {
        if(empty($val)) {
            return '';
        }
        switch (gettype($val)) {
            case 'NULL':
                return NULL;
                break;
            case 'string':
                return addslashes($val);
                break;
            case 'boolean':
            case 'integer':
            case 'double':
                return $val;
                break;
            case 'array':
                return 'Array';
                break;
            case 'object':
                return 'Object';
                break;
        }
        return addslashes((string)$val);
    }

    /**
     * 关闭当前连接
     */
    public function close()
    {
        return MySQLiProxy::close((string)$this->alias);
    }

    /**
     * 智能获取连接资源
     *
     * @param string $sql
     * @param null $flag null智能选择 false使用从库 true使用主库
     * @return MySQLiDriver
     */
    protected function getConnection($flag = null, $sql = '')
    {
        if($this->inTransaction === true || $flag === true || $this->flag === true) {
            $flag = true;
        } elseif($flag === false || $this->flag === false) {
            $flag = false;
        }
        return MySQLiProxy::get((string)$this->alias, $flag, (string)$sql);
    }
}